﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __PACKET_H__
#define __PACKET_H__

#pragma once

#include "datadef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 网络组包数据识别头部和判断数据包是否接收完成
//#define __USE_DEFAULT_NET_BUILD__
class CFrameworkNETPackBuild : public CNETTraits::CNETPackBuild
{
public:
    enum PACK_HEAD_FLAG
    {
        PACK_HEAD_FLAG_V3 = 0x88058025,
    };
    typedef struct tagPACK_HEAD
    {
        UInt    uFlag;
        UInt    uSize;
    }PACK_HEAD, *PPACK_HEAD;
public:
    static size_t GetHeadSize(void)
    {
        return sizeof(PACK_HEAD);
    }
    static void   BuildHead(CBufWriteStream& Stream, UInt uSize)
    {
        PACK_HEAD Head;
        Head.uFlag = (UInt)PACK_HEAD_FLAG_V3;
        Head.uSize = uSize;
        Stream.Write(&Head, sizeof(PACK_HEAD));
    }
public:
    CFrameworkNETPackBuild(void)
    {
        DEV_DUMP(TF("Server Framework PackBuild Created"));
    }
    virtual ~CFrameworkNETPackBuild(void)
    {
        DEV_DUMP(TF("Server Framework PackBuild Destroyed"));
    }

    // send package head ptr, package data size
    // return package all size, include package head
    virtual size_t Build(PByte pPack, size_t stSize) OVERRIDE
    {
        PPACK_HEAD pHead = reinterpret_cast<PPACK_HEAD>(pPack);
        pHead->uFlag = (UInt)PACK_HEAD_FLAG_V3;
        pHead->uSize = (UInt)stSize;
        return (size_t)(stSize + sizeof(PACK_HEAD));
    }
    // check recv package valid check
    // return package all size, include package head
    // return 0 when check fail
    virtual size_t Check(PByte pPack, size_t stSize) OVERRIDE
    {
        PPACK_HEAD pHead = reinterpret_cast<PPACK_HEAD>(pPack);
        if (pHead->uFlag == (UInt)PACK_HEAD_FLAG_V3)
        {
            return (pHead->uSize + GetHeadSize());
        }
        DEV_DUMP(TF("Server Framework PackBuild Check Failed %08X---%08X[%d]"), pHead->uFlag, pHead->uSize, stSize);
        return 0;
    }
    // package head size
    virtual size_t HeadSize(void) OVERRIDE
    {
        return sizeof(PACK_HEAD);
    }
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 信令包数据头 : 按信令号将数据包转换成对应包实例
class CPAKHeadBase : public CEventBase
{
public:
    /// 基于CPAKHeadBase的数据包创建入口
    /// @param[in] CStream& 数据流
    /// @retval CPAKHeadBase* 创建成功返回指向CPAKHeadBase实例的指针, 失败返回nullptr
    static CPAKHeadBase* Create(CStream&);
public:
    /// 构造
    /// @param[in] 信令号
    /// @param[in] 数据包类型
    CPAKHeadBase(UEVENT uEvent = 0, UTYPE uType = PAKT_NONE, UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CPAKHeadBase(void) OVERRIDE;

    CPAKHeadBase(const CPAKHeadBase& aSrc);
    CPAKHeadBase& operator=(const CPAKHeadBase& aSrc);
    /// 计算数据包大小
    /// @remarks AdjustSize调用Length返回值填充m_uSize
    virtual size_t Length(void) OVERRIDE;
    /// 将数据包序列化
    /// @param[in] CStream& 数据流
    virtual void   Serialize(CStream&) OVERRIDE;
    /// 获取信令号
    UEVENT   GetEvent(void);
    /// 设置信令号
    void     SetEvent(UEVENT uEvent);
    /// 获取类型
    UTYPE    GetType(void);
    /// 设置类型
    void     SetType(UTYPE uType);
    /// 获取ACK
    UACK     GetAck(void);
    /// 设置ACK
    void     SetAck(UACK uAck);

    /// 获取srv-id
    USRVID   GetSrvId(void);
    /// 设置ACK
    void     SetSrvId(USRVID uSrvId);

    void     ResetHead(UEVENT uEvent, UTYPE uType, UACK uAck = (UACK)ERRORD_OKAY);
private:
    UEVENT   m_uEvent; ///< 信令号
    UTYPE    m_uType;  ///< 发送数据包类型
    UACK     m_uAck;   ///< ERRORD_OKAY-成功, 非零值表示具体错误编号
    USRVID   m_uSrvId;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 信令包数据头模板
template <UEVENT uEvent, UTYPE uType>
class CTPAKHeadBase : public CPAKHeadBase
{
public:
    CTPAKHeadBase(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKHeadBase(void) OVERRIDE;

    CTPAKHeadBase(const CTPAKHeadBase& aSrc);
    CTPAKHeadBase& operator=(const CTPAKHeadBase& aSrc);
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 网络层解析数据派发定义
typedef CNetworkEventHandler<CNETDispatch<CPAKHeadBase, false>> CPAKHandler;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "packet.inl"

#endif // __PACKET_H__
